package com.chenl.interview.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chenl.interview.common.ErrorCode;
import com.chenl.interview.common.ThrowUtils;
import com.chenl.interview.constant.CommonConstant;
import com.chenl.interview.constant.RedisConstant;
import com.chenl.interview.constant.UserConstant;
import com.chenl.interview.exception.BusinessException;
import com.chenl.interview.mapper.UserMapper;
import com.chenl.interview.model.dto.user.UserQueryRequest;
import com.chenl.interview.model.entity.User;
import com.chenl.interview.model.vo.LoginUserVO;
import com.chenl.interview.satoken.DeviceUtils;
import com.chenl.interview.service.UserService;
import com.chenl.interview.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBitSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

import static com.chenl.interview.constant.UserConstant.USER_LOGIN_STATE;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 盐值，混淆密码
     */
    public static final String SALT = "chen_interview";

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 用户注册
     * @param userAccount
     * @param userPassword
     * @param checkPassword
     * @return
     */
    @Override
    public Long register(String userAccount, String userPassword, String checkPassword) {
        //参数校验
        if (StringUtils.isAnyBlank(userAccount,userPassword,checkPassword)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"参数为空");
        }
        if(userAccount.length() < 4){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户密码过短");
        }
        if(!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"前后密码不一致");
        }

        synchronized (userAccount.intern()){
            //查询当前注册用户账号是否存在
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount",userAccount);
            Long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"该账号已存在");
            }

            //当前账号不存在，存入数据库
            //密码加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            boolean save = this.save(user);
            if (!save){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"注册失败");
            }
            return user.getId();
        }
    }

    /**
     * 用户登录
     * @param userAccount
     * @param userPassword
     * @param header
     * @return
     */
    @Override
    public LoginUserVO login(String userAccount, String userPassword,String header) {
        //参数校验
        if (StringUtils.isAnyBlank(userAccount,userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"参数为空");
        }
        LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda()
                                                        .eq(User::getUserAccount,userAccount);

        User user = this.baseMapper.selectOne(lambdaQueryWrapper);
        if (user == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"用户不存在");
        }
        if (UserConstant.BAN_ROLE.equals(user.getUserRole())){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"该账号已被封禁");
        }
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        if (!user.getUserPassword().equals(encryptPassword)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"密码错误");
        }

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime vipExpiresAt = user.getVipExpiresAt();
        //会员过期
        if (vipExpiresAt != null && now.isAfter(vipExpiresAt)){
            user.setIsVip(0);
            //todo 提醒用户续费
        }

        LoginUserVO loginUserVO = BeanUtil.copyProperties(user, LoginUserVO.class);

        //sa-Token登录，并指定登录设备，实现同端互斥登录
        StpUtil.login(user.getId(), DeviceUtils.getRequestDevice(header));
        //存储登录用户相关信息
        StpUtil.getSession().set(USER_LOGIN_STATE,user);

        return loginUserVO;
    }


    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        //参数校验
        ThrowUtils.throwIf(userQueryRequest == null,new BusinessException(ErrorCode.PARAMS_ERROR,"参数为空"));

        //获取参数
        Long id = userQueryRequest.getId();
        String unionId = userQueryRequest.getUnionId();
        String mpOpenId = userQueryRequest.getMpOpenId();
        String userName = userQueryRequest.getUserName();
        String userProfile = userQueryRequest.getUserProfile();
        String userRole = userQueryRequest.getUserRole();
        Integer isVip = userQueryRequest.getIsVip();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();

        //构造查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        wrapper.eq(id != null,"id",id);
        wrapper.eq(StringUtils.isNotBlank(unionId),"unionId",unionId);
        wrapper.eq(StringUtils.isNotBlank(mpOpenId),"mpOpenId",mpOpenId);
        wrapper.eq(StringUtils.isNotBlank(userRole),"userRole",userRole);
        boolean flag = isVip != null && (isVip == 0 || isVip == 1) ? true : false;
        wrapper.eq(flag,"isVip",isVip);
        wrapper.like(StringUtils.isNotBlank(userProfile),"userProfile",userProfile);
        wrapper.like(StringUtils.isNotBlank(userName),"userName",userName);
        wrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);

        return wrapper;
    }

    @Override
    public boolean addUserSignIn(Long id) {
        LocalDate now = LocalDate.now();
        String key = RedisConstant.getUserSignInRedisKey(now.getYear(), id);
        RBitSet bitSet = redissonClient.getBitSet(key);

        //获取当天是当年的第几天（从1开始计数）
        int offset = now.getDayOfYear();
        //检查当天是否签到
        if (!bitSet.get(offset)) {
            //当天未签到则签到
            return bitSet.set(offset,true);
        }
        //已签到返回true
        return true;
    }

    @Override
    public List<Integer> getUserSingIn(Long userId, Integer year) {
        if (year == null) {
            LocalDate date = LocalDate.now();
            year = date.getYear();
        }
        String key = RedisConstant.getUserSignInRedisKey(year, userId);
        //signInBitSet是Redisson客户端与redis交互的对象
        RBitSet signInBitSet = redissonClient.getBitSet(key);
        // 加载 BitSet 到内存中，避免后续读取时发送多次请求
        BitSet bitSet = signInBitSet.asBitSet();
        // 统计签到的日期
        List<Integer> dayList = new ArrayList<>();
        // 从索引 0 开始查找下一个被设置为 1 的位
        int index = bitSet.nextSetBit(0);
        while (index >= 0) {
            dayList.add(index);
            // 查找下一个被设置为 1 的位
            index = bitSet.nextSetBit(index + 1);
        }
        return dayList;
    }
}
